﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace Hsenl {
    [Combination]
    [FrameworkMember]
    public abstract class Combination {
        private static Info _info;

        internal static Dictionary<Type, Combination> TypeCombinationMap => _info.typeCombinationMap;
        internal static List<SingleCombination> AllSingleCombinations => _info.allSingleCombinations; // 所有单组合
        internal static ComponentTypeCacher TotulSingleCombinationsTypeCacher => _info.totulSingleCombinationsTypeCacher; // 所有单组合的类型集合
        internal static List<CompositeCombination> AllCompositeCombinations => _info.allCompositeCombinations; // 所有的复合组合
        internal static List<CompositeCombination> AllExtremeCompositeCombinations => _info.allExtremeCompositeCombinations; // 所有极端的复合组合
        internal static ComponentTypeCacher TotulCompositeCombinationsLowestTypeCacher => _info.totulCompositeCombinationsLowestTypeCacher; // 所有复合组合的最低位的类型集合
        internal static List<Combination> AllCombinations => _info.allCombinations; // 所有的组合, 包括单组合和复合组合
        internal static MultiList<int, int> Overrides => _info.overrides; // key: 覆盖者id, value: 被覆盖者id
        internal static MultiList<int, int> InverseOverrides => _info.inverseOverrides; // key: 被覆盖者id, value: 覆盖者id

        [OnEventSystemInitialized]
        private static void InitCombinations() {
            Unload();
            _info = new Info();
            var types = EventSystem.GetTypesOfAttribute(typeof(CombinationAttribute));
            foreach (var type in types) {
                var combination = CreateCombination(type);
                if (combination == null)
                    continue;

                AddCombination(combination);
            }

            TidyUpAllCombinations();
        }

        [OnEventSystemUnload]
        private static void Unload() {
            _info?.Dispose();
            _info = null;
        }

        private static Combination CreateCombination(Type type) {
            if (type.IsGenericType || type.IsAbstract)
                return null;

            var combination = (Combination)Activator.CreateInstance(type);
            var genericType = AssemblyHelper.GetGenericBaseClass(type);
            if (genericType == null) {
                Log.Error($"combination must inherit from a generic combination '{type.Name}'");
                return null;
            }

            combination.memberTypes = genericType.GetGenericArguments();
            var hashset = new HashSet<Type>();
            foreach (var memberType in combination.memberTypes) {
                if (!hashset.Add(memberType)) {
                    throw new Exception($"There cannot be duplicate members in a combination! '{memberType.Name}'");
                }
            }

            switch (combination) {
                case SingleCombination: {
                    combination.singleTypeCacher = Component.CombineComponentType(combination.memberTypes);
                    break;
                }

                case CompositeCombination compositeCombination: {
                    var groups = compositeCombination.GetTypeGroups();
                    if (groups[0] == null || groups[groups.Length - 1] == null)
                        // 对于复合组合来说, 头尾必须指定有效的组合
                        throw new Exception($"composite combination group first or last can't be null.'{compositeCombination.GetType()}'");

                    var len = groups.Length;
                    combination.compositeTypeCacher = new ComponentTypeCacher[len];
                    for (int i = 0; i < len; i++) {
                        var group = groups[i];
                        if (group == null)
                            throw new NullReferenceException($"composite group is null! '{combination.GetType()}'");

                        combination.compositeTypeCacher[i] = Component.CombineComponentType(groups[i]);
                    }

                    break;
                }
            }

            return combination;
        }

        private static void AddCombination(Combination combination) {
            var type = combination.GetType();
            if (_info.typeCombinationMap.ContainsKey(type))
                return;

            combination.id = _info.allCombinations.Count;
            switch (combination) {
                case SingleCombination singleCombination: {
                    _info.allSingleCombinations.Add(singleCombination);
                    break;
                }
                case CompositeCombination compositeCombination: {
                    _info.allCompositeCombinations.Add(compositeCombination);
                    if (compositeCombination.GetCombinationMode() == CompositeCombinationMode.Extreme) {
                        compositeCombination.CompositeCombinationMode = CompositeCombinationMode.Extreme;
                        _info.allExtremeCompositeCombinations.Add(compositeCombination);
                    }

                    break;
                }
                default:
                    throw new ArgumentOutOfRangeException();
            }

            _info.typeCombinationMap.Add(type, combination);
            _info.allCombinations.Add(combination);
        }

        // 整理所有组合的需要用到的信息
        private static void TidyUpAllCombinations() {
            _info.totulSingleCombinationsTypeCacher = ComponentTypeCacher.CreateEmpty();
            foreach (var combination in _info.allSingleCombinations) {
                _info.totulSingleCombinationsTypeCacher.Add(combination.singleTypeCacher);
            }

            _info.totulCompositeCombinationsLowestTypeCacher = ComponentTypeCacher.CreateEmpty();
            foreach (var combination in _info.allCompositeCombinations) {
                _info.totulCompositeCombinationsLowestTypeCacher.Add(combination.compositeTypeCacher[0]);
            }

            // 缓存所有CombinationOverride
            _info.overrides.Clear();
            foreach (var combination in _info.allCombinations) {
                var overrides = combination.GetOverrideCombinations();
                if (overrides == null)
                    continue;

                foreach (var overrideType in overrides) {
                    var overrideCombination = _info.typeCombinationMap[overrideType];
                    if (combination is not SingleCombination || overrideCombination is not SingleCombination)
                        throw new Exception("Only single combination can be use override opeartion.");

                    _info.overrides.Add(combination.id, overrideCombination.id);
                }
            }

            // 检查CombinationOverride是否存在冲突问题(两个组合相互覆盖)
            _info.inverseOverrides.Clear();
            foreach (var kv in _info.overrides) {
                foreach (var overrideUniqueId in kv.Value) {
                    if (_info.overrides.TryGetValue(overrideUniqueId, out var list)) {
                        if (list.Contains(kv.Key)) {
                            var combination1 = _info.allCombinations[kv.Key];
                            var combination2 = _info.allCombinations[overrideUniqueId];
                            throw new Exception(
                                $"<override combination conflict exists, '{combination1.GetType().Name} - {combination1.id}' '{combination1.GetType().Name} - {combination2.id}'>");
                        }
                    }

                    _info.inverseOverrides.Add(overrideUniqueId, kv.Key);
                }
            }

            // 把 singleCombinationInfos 按照规律缓存起来
            _info.componentSingleCombinationInfos.Clear();
            foreach (var combination in _info.allSingleCombinations) {
                foreach (var memberType in combination.memberTypes) {
                    var componentTypeIndex = Component.GetComponentIndex(memberType);
                    if (!_info.componentSingleCombinationInfos.TryGetValue(componentTypeIndex, out var info)) {
                        info = new SingleCombinationInfo();
                        _info.componentSingleCombinationInfos[componentTypeIndex] = info;
                    }

                    info.relatedCombinations.Add(combination);
                }
            }

            // 整理 componentInfos
            foreach (var kv in _info.componentSingleCombinationInfos) {
                var combinInfo = kv.Value;

                combinInfo.relatedCombinations.Sort((combination1, combination2) => combination1.memberTypes.Length.CompareTo(combination2.memberTypes.Length));
            }

            // 把 compositeCombinationInfos 按照规律缓存起来
            _info.componentCompositeCombinationInfos.Clear();
            foreach (var combination in _info.allCompositeCombinations) {
                var groups = combination.GetTypeGroups();
                for (int i = 0; i < groups.Length; i++) {
                    var group = groups[i];
                    foreach (var type in group) {
                        var componentTypeIndex = Component.GetComponentIndex(type);
                        if (!_info.componentCompositeCombinationInfos.TryGetValue(componentTypeIndex, out var info)) {
                            info = new CompositeCombinationInfo();
                            _info.componentCompositeCombinationInfos[componentTypeIndex] = info;
                        }

                        info.relatedCombinations.Add((i, combination));
                    }
                }
            }
        }

        internal static bool TryGetSingleInfo(int componentIndex, out SingleCombinationInfo singleCombinationInfo) {
            return _info.componentSingleCombinationInfos.TryGetValue(componentIndex, out singleCombinationInfo);
        }

        internal static bool TryGetCompositeInfo(int componentIndex, out CompositeCombinationInfo compositeCombinationInfo) {
            return _info.componentCompositeCombinationInfos.TryGetValue(componentIndex, out compositeCombinationInfo);
        }

        internal int id; // 唯一id, combin和cross之间也不会重复
        internal Type[] memberTypes;
        internal ComponentTypeCacher singleTypeCacher;
        internal ComponentTypeCacher[] compositeTypeCacher;

        internal int actionCounter; // 事件计数器
        private readonly Dictionary<int, Delegate> _actions = new();
        protected readonly Dictionary<int, int> actionCounters = new(); // 记录每个形成的组合的事件数, 用以判断每个组合是不是忘了 -= action

        private Dictionary<int, object> _userTokens = new();

        internal void Form_Internal(IList<Component> components) {
            this.Form(components);
        }

        internal void Break_Internal(List<Component> components) {
            this.Break(components);
        }

        protected virtual int Form(IList<Component> components) {
            this.actionCounter = 0;
            return 0;
        }

        protected virtual int Break(IList<Component> components) {
            this.actionCounter = 0;
            return 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        protected abstract int GetComponentCombineHashCode();

        protected virtual Type[] GetOverrideCombinations() {
            return null;
        }

        protected T EnqueueAction<T>(T action) where T : Delegate {
            var hashcode = this.GetComponentCombineHashCode();
            hashcode = HashCode.Combine(hashcode, this.actionCounter++);
            if (!this._actions.TryAdd(hashcode, action))
                throw new Exception($"combination is already has combined'{typeof(T)}' '{this.GetType()}'");
            return action;
        }

        protected T DequeueAction<T>() where T : Delegate {
            var hashcode = this.GetComponentCombineHashCode();
            hashcode = HashCode.Combine(hashcode, this.actionCounter++);
            if (!this._actions.Remove(hashcode, out var action)) {
                // enqueue和dequeue必须一一对应, 顺序也不能错
                throw new NullReferenceException("action is null, check whether EnqueueAction and DequeueAction one-to-one correspondenceto each other");
            }

            return (T)action;
        }

        protected T GetUserToken<T>() where T : class, new() {
            var hashcode = this.GetComponentCombineHashCode();
            if (!this._userTokens.TryGetValue(hashcode, out var value)) {
                value = ObjectPool.Rent<T>();
                // value = new T();
                this._userTokens[hashcode] = value;
            }
            else {
                if (value is not T)
                    throw new Exception($"UserToken is already assigned to '{value.GetType()}' in '{this.GetType()}'");
            }

            return (T)value;
        }

        internal bool RemoveUserToken() {
            var hashcode = this.GetComponentCombineHashCode();
            var b = this._userTokens.Remove(hashcode, out var obj);
            if (obj is IDisposable disposable)
                disposable.Dispose();
            ObjectPool.Return(obj);
            return b;
        }

        internal bool RemoveUserToken<T>(out T token) {
            var hashcode = this.GetComponentCombineHashCode();
            if (this._userTokens.Remove(hashcode, out var obj)) {
                try {
                    token = (T)obj;
                    return true;
                }
                catch (Exception e) {
                    Log.Error(e);
                }
            }

            if (obj is IDisposable disposable)
                disposable.Dispose();
            ObjectPool.Return(obj);
            token = default;
            return false;
        }

        public static T GetCombination<T>() where T : Combination {
            TypeCombinationMap.TryGetValue(typeof(T), out var combination);
            return combination as T;
        }

        public static Combiner GetCombiner(Entity entity) {
            return Combiner.GetCombiner(entity);
        }

        internal class Info {
            internal readonly Dictionary<Type, Combination> typeCombinationMap = new();

            internal readonly List<SingleCombination> allSingleCombinations = new(); // 所有的单组合
            internal ComponentTypeCacher totulSingleCombinationsTypeCacher; // 所有单组合的类型集合
            internal readonly List<CompositeCombination> allCompositeCombinations = new(); // 所有的复合组合
            internal readonly List<CompositeCombination> allExtremeCompositeCombinations = new(); // 所有极端的复合组合
            internal ComponentTypeCacher totulCompositeCombinationsLowestTypeCacher; // 所有复合组合的最低位的类型集合
            internal readonly List<Combination> allCombinations = new(); // 所有的组合, 包括单组合和复合组合

            internal readonly MultiList<int, int> overrides = new(); // key: 覆盖者id, value: 被覆盖者id
            internal readonly MultiList<int, int> inverseOverrides = new(); // key: 被覆盖者id, value: 覆盖者id

            internal readonly Dictionary<int, SingleCombinationInfo>
                componentSingleCombinationInfos = new(); // key: componentId, value: 保存所有和该组件有关的单组合信息

            internal readonly Dictionary<int, CompositeCombinationInfo>
                componentCompositeCombinationInfos = new(); // key: componentId, value: 保存所有和该组件有关的复合组合信息

            public void Dispose() {
                this.typeCombinationMap.Clear();
                this.allSingleCombinations.Clear();
                this.totulSingleCombinationsTypeCacher?.Clear();
                this.allCompositeCombinations.Clear();
                this.allExtremeCompositeCombinations.Clear();
                this.totulCompositeCombinationsLowestTypeCacher?.Clear();
                this.allCombinations.Clear();

                this.overrides.Clear();
                this.inverseOverrides.Clear();

                foreach (var kv in this.componentSingleCombinationInfos) {
                    kv.Value.relatedCombinations.Clear();
                }

                this.componentSingleCombinationInfos.Clear();
                foreach (var kv in this.componentCompositeCombinationInfos) {
                    kv.Value.relatedCombinations.Clear();
                }

                this.componentCompositeCombinationInfos.Clear();
            }
        }

        internal class SingleCombinationInfo {
            public readonly List<SingleCombination> relatedCombinations = new(); // 某个组件相关的所有单组合
        }

        internal class CompositeCombinationInfo {
            public readonly List<(int position, CompositeCombination combination)> relatedCombinations = new(); // 某个组件相关的所有复合组合 (int: 该组件在每个组合中, 所处的位置)
        }
    }

    public enum CustomCompositeReturn {
        NoHanle, // 不处理, 交给系统决定

        Succ,
        Fail,
        Next, // 跳过当前entity, 不算失败, 继续尝试下一个entity
    }
}